package se.webbzon.oschi01.region;

import java.awt.Point;
import java.awt.geom.Point2D;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;



//import org.bolet.jgz.ZlibInputStream;
import se.webbzon.oschi01.nbt.NBTDataInputStreamReader;
import se.webbzon.oschi01.nbt.NBTException;
import se.webbzon.oschi01.nbt.Tag;
import se.webbzon.oschi01.singleton.Singletons;
import se.webbzon.oschi01.world.Chunk;
import se.webbzon.oschi01.world.CoordinateMediator;

import com.googlecode.asmack.connection.impl.ZLibInputStream;

public class URLChunk implements Chunk {
	
	private final int sectorSize = 4096;
	
	private final URL regionUrl;
	
	private final int offset, sectorCount;
	
	private final CoordinateMediator mediator = new CoordinateMediator();
	
	private long timestamp;
	
	URLChunk(URL regionUrl, int offset, int sectorCount, int timestamp, Point coordinate) {
		this.regionUrl = regionUrl;
		this.offset = offset;
		this.sectorCount = sectorCount;
		this.timestamp = timestamp;
		mediator.setCoordinate(coordinate.x, coordinate.y);
	}
	
	public URL getRegionUrl() {
		return regionUrl;
	}
	
	public int getOffset() {
		return offset;
	}
	
	public int getSectorCount() {
		return sectorCount;
	}
	
	@Override public Point2D getCoordinate() {
		return mediator.getCoordinate();
	}
	
	@Override public void setCoordinate(int x, int z) {
		mediator.setCoordinate(x, z);
	}

	@Override public long getTimestamp() {
		return timestamp;
	}

	@Override public void setTimestamp(long timestamp) {
		this.timestamp = timestamp;
	}
	
	@Override public Tag<?> getRoot() throws NBTException {
		final Tag<?> root = mediator.getRoot();
		if (root != null)
			return root;
		
		byte[] chunk = null;
		try (InputStream is = regionUrl.openStream()) {
			is.skip(sectorSize * offset);
			
			int remaining = sectorSize * sectorCount;
			int off = 0;
			chunk = new byte[remaining];
			while (remaining > 0) {
				int r = is.read(chunk, off, remaining);
				off += r;
				remaining -= r;
			}
		} catch (IOException e) {
			throw new NBTException(e);
		}
		
		try {
			// Read chunk
			InputStream ais = new ByteArrayInputStream(chunk);
			ais.skip(4); // Skip actual size
			byte format = (byte) ais.read();
		
			// Decorate with extractor
			InputStream eis;
			if (format == 2)
				eis = new ZLibInputStream(ais);
			else
				throw new IOException("Unsupported chunk compression format (" + format + ") for chunk " + this + " at offset " + offset + " of " + sectorCount + " sectors.");
		
			NBTDataInputStreamReader reader = Singletons.instance(NBTDataInputStreamReader.class);
			DataInputStream dis = new DataInputStream(eis);
			Tag<?> r = reader.read(dis);
			// ais.close();
			mediator.attachTo(r);
			return r;
		} catch (IOException e) {
			throw new NBTException(e);
		}
	}
	
	@Override public String toString() {
		Point2D coord = mediator.getCoordinate();
		return "[x=" + coord.getX() + ",z="+ coord.getY() + "]";
	}

	@Override public boolean isLoaded() {
		return mediator.getRoot() != null;
	}

	@Override public void free() {
		mediator.detach();
	}
	
}
